<?php

namespace app\utils\hook;

class Hook
{
    /**
     * 监听者
     */
    protected $listener = [];

    /**
     * 事件别名
     * @var array
     */
    protected $bind = [
    ];

    /**
     * 获取监听数据
     * @return array
     */
    public function getListener(){
        return $this->listener;
    }

    /**
     * 获取绑定数据
     * @return array
     */
    public function getBind(){
        return $this->bind;
    }

    /**
     * 批量注册事件监听
     */
    public function listenEvents(array $events)
    {
        foreach ($events as $event => $listeners) {
            if (isset($this->bind[$event])) {
                $event = $this->bind[$event];
            }
            $listener=isset($this->listener[$event])?$this->listener[$event]:array();
            $this->listener[$event] = array_merge($listener, $listeners);
        }
        return $this;
    }

    /**
     * 注册事件监听
     */
    public function listen($event, $listener)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        $this->listener[$event][] = $listener;
        return $this;
    }

    /**
     * 是否存在事件监听
     */
    public function hasListener($event)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        return isset($this->listener[$event]);
    }

    /**
     * 移除事件监听
     */
    public function remove($event)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        unset($this->listener[$event]);
    }

    /**
     * 绑定事件别名
     */
    public function bind(array $events)
    {
        $this->bind = array_merge($this->bind, $events);
        return $this;
    }

    /**
     * 触发事件
     */
    public function trigger($event, $params = null, $once = false)
    {
        $final_arr = logInit();
        if ($final_arr["error_no"] == 0) {
            if (is_object($event)) {
                $params = $event;
                $event  = get_class($event);
            }
            if (isset($this->bind[$event])) {
                $event = $this->bind[$event];
            }
            $result    = array();
            $listeners = isset($this->listener[$event]) ?$this->listener[$event]:array();
            if (strpos($event, '.')) {
                list($prefix, $event) = explode('.', $event, 2);
                if (isset($this->listener[$prefix . '.*'])) {
                    $listeners = array_merge($listeners, $this->listener[$prefix . '.*']);
                }
            }
            $listeners = array_unique($listeners, SORT_REGULAR);
            foreach ($listeners as $key => $listener) {
                if ($final_arr["error_no"] == 0) {
                    $checkResult = $this->dispatch($listener, $params);
                    if ($checkResult["error_no"] == 0) {
                        $result[$key] = $checkResult["result"];
                    } else {
                        $final_arr = logCallErrorMsg($final_arr, $checkResult);
                    }
                }
                if ($final_arr["error_no"] || $once) {
                    break;
                }
            }
            if ($final_arr["error_no"] == 0) {
                $final_arr["result"]=$once ? end($result) : $result;
            }
        }
        return $final_arr;
    }

    /**
     * 触发事件(只获取一个有效返回值)
     */
    public function until($event, $params = null)
    {
        return $this->trigger($event, $params, true);
    }

    /**
     * 执行事件调度
     */
    protected function dispatch($event, $params = null)
    {
        $final_arr = logInit();
        if ($final_arr["error_no"] == 0) {
            if (!is_string($event)) {
                $call = $event;
            }else {
                $obj  = app($event);
                $call = [$obj, 'handle'];
            }
            $params=is_null($params)?array():(is_array($params)?$params:array($params));
            $checkResult = call_user_func_array($call, $params);
            if ($checkResult["error_no"] == 0) {
                $final_arr["result"] = $checkResult["result"];
            } else {
                $final_arr = logCallErrorMsg($final_arr, $checkResult);
            }
        }
        return $final_arr;
    }
}
